package com.abso.sunlight.explorer.editors;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.browser.IWebBrowser;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.part.EditorPart;

import com.abso.sunlight.api.Legislator;
import com.abso.sunlight.explorer.CongressExplorerPlugin;

/**
 * The details editor displays detailed information about a specific legislator.
 */
public class LegislatorDetailsEditor extends EditorPart {

    /** The unique editor's identifier. */
    public static final String ID = LegislatorDetailsEditor.class.getName();

    /** The image displaying the legislator's photo (to be disposed closing the editor). */
    private Image photoImage;

    @Override
    public void doSave(IProgressMonitor monitor) {
    }

    @Override
    public void doSaveAs() {
    }

    @Override
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        setSite(site);
        setInput(input);
        setPartName(input.getName());
    }

    @Override
    public boolean isDirty() {
        return false;
    }

    @Override
    public boolean isSaveAsAllowed() {
        return false;
    }

    @Override
    public void createPartControl(Composite parent) {
        Legislator legislator = getLegislator();
        Color bgColor = parent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
        Color fgColor = parent.getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
        Color nameFgColor = JFaceResources.getColorRegistry().get(JFacePreferences.COUNTER_COLOR);
        Color linkFgColor = JFaceColors.getHyperlinkText(parent.getDisplay());

        /* creates a composite to display the scrollable information list */
        final ScrolledComposite scrollComposite = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.BORDER);
        setColors(scrollComposite, bgColor, fgColor);
        final Composite mainArea = new Composite(scrollComposite, SWT.NONE);
        setColors(mainArea, bgColor, fgColor);
        mainArea.setLayout(new GridLayout(2, false));
        scrollComposite.setContent(mainArea);
        scrollComposite.setExpandVertical(true);
        scrollComposite.setExpandHorizontal(true);
        scrollComposite.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(ControlEvent e) {
                Rectangle r = scrollComposite.getClientArea();
                scrollComposite.setMinSize(mainArea.computeSize(r.width, SWT.DEFAULT));
            }
        });

        /* creates the name field */
        Composite nameArea = new Composite(mainArea, SWT.NONE);
        setColors(nameArea, bgColor, fgColor);
        GridData nameGridData = new GridData(SWT.FILL, SWT.FILL, true, false);
        nameGridData.horizontalSpan = 2;
        nameArea.setLayoutData(nameGridData);
        nameArea.setLayout(new GridLayout(2, false));
        Label titleLabel = new Label(nameArea, SWT.NONE);
        setColors(titleLabel, bgColor, nameFgColor);
        titleLabel.setFont(JFaceResources.getHeaderFont());
        titleLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
        titleLabel.setText(legislator.getTitle().toString());
        Text nameText = new Text(nameArea, SWT.NONE);
        nameText.setEditable(false);
        setColors(nameText, bgColor, fgColor);
        nameText.setFont(JFaceResources.getHeaderFont());
        nameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        List<String> nameTokens = new ArrayList<String>(5);
        nameTokens.add(legislator.getFirstName());
        nameTokens.add(legislator.getMiddleName());
        if (!StringUtils.isBlank(legislator.getNickname())) {
            nameTokens.add("\"" + legislator.getNickname() + "\"");
        }
        nameTokens.add(legislator.getLastName());
        nameTokens.add(legislator.getNameSuffix());
        nameText.setText(StringUtils.join(nameTokens, " "));

        /* creates the photo field */
        if (!StringUtils.isBlank(legislator.getBioguideId())) {
            try {
                ImageDescriptor photoImageDescr = ImageDescriptor.createFromURL(new URL("http://bioguide.congress.gov/bioguide/photo/"
                        + legislator.getBioguideId().charAt(0) + "/" + legislator.getBioguideId() + ".jpg "));
                if ((photoImageDescr != null) && !(photoImageDescr.equals(ImageDescriptor.getMissingImageDescriptor()))) {
                    photoImage = photoImageDescr.createImage();
                }
            } catch (MalformedURLException e1) {
                // this should not happen
            }
            if (photoImage != null) {
                Rectangle bounds = photoImage.getBounds();
                if (bounds.width > 10) { // avoid to show the default missing image
                    Label imageLabel = new Label(mainArea, SWT.NONE);
                    GridData imageLayoutData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
                    imageLayoutData.horizontalSpan = 2;
                    imageLabel.setLayoutData(imageLayoutData);
                    imageLabel.setImage(photoImage);
                }
            }
        }

        /* adds additional property fields */
        addStringProperty(mainArea, "Party", legislator.getParty().toString(), bgColor, fgColor, nameFgColor);
        addStringProperty(mainArea, "State", legislator.getState().toString(), bgColor, fgColor, nameFgColor);
        addStringProperty(mainArea, "District", legislator.getDistrict().toString(), bgColor, fgColor, nameFgColor);
        addStringProperty(mainArea, "In Office", legislator.isInOffice() ? "Active" : "Inactive", bgColor, fgColor, nameFgColor);
        addStringProperty(mainArea, "Gender", legislator.getGender().toString(), bgColor, fgColor, nameFgColor);
        addStringProperty(mainArea, "Office Phone", legislator.getPhone().toString(), bgColor, fgColor, nameFgColor);
        addStringProperty(mainArea, "Office Fax", legislator.getFax().toString(), bgColor, fgColor, nameFgColor);
        addStringProperty(mainArea, "Office Address", legislator.getCongressOffice(), bgColor, fgColor, nameFgColor);
        addURLProperty(mainArea, "Web Site", legislator.getWebSite(), bgColor, linkFgColor, nameFgColor);
        addURLProperty(mainArea, "Web Contact Form", legislator.getWebContactForm(), bgColor, linkFgColor, nameFgColor);
        addURLProperty(mainArea, "Email", legislator.getEmail(), bgColor, linkFgColor, nameFgColor, true);
        addStringProperty(mainArea, "Bioguide ID", legislator.getBioguideId(), bgColor, fgColor, nameFgColor);
        addURLProperty(mainArea, "", getURL("http://bioguide.congress.gov/scripts/biodisplay.pl?index=${id}", legislator
                .getBioguideId()), bgColor, linkFgColor, nameFgColor);
        addStringProperty(mainArea, "Vote Smart ID", legislator.getVoteSmartId(), bgColor, fgColor, nameFgColor);
        addURLProperty(mainArea, "", getURL("http://votesmart.org/bio.php?can_id=${id}", legislator.getVoteSmartId()), bgColor,
                linkFgColor, nameFgColor);
        addStringProperty(mainArea, "FEC ID", legislator.getFecId(), bgColor, fgColor, nameFgColor);
        addURLProperty(mainArea, "", getURL("http://query.nictusa.com/cgi-bin/can_detail/${id}", legislator.getFecId()), bgColor,
                linkFgColor, nameFgColor);
        addStringProperty(mainArea, "GovTrack.us ID", legislator.getGovTrackId(), bgColor, fgColor, nameFgColor);
        addURLProperty(mainArea, "", getURL("http://www.govtrack.us/congress/person.xpd?id=${id}", legislator.getGovTrackId()),
                bgColor, linkFgColor, nameFgColor);
        addStringProperty(mainArea, "CRP ID", legislator.getCrpId(), bgColor, fgColor, nameFgColor);
        addURLProperty(mainArea, "", getURL("http://www.opensecrets.org/politicians/summary.php?cid=${id}", legislator.getCrpId()),
                bgColor, linkFgColor, nameFgColor);
        addStringProperty(mainArea, "Eventful.com ID", legislator.getEventfulId(), bgColor, fgColor, nameFgColor);
        addURLProperty(mainArea, "", getURL("http://eventful.com/performers/${id}", legislator.getEventfulId()), bgColor, linkFgColor,
                nameFgColor);
        addStringProperty(mainArea, "Twitter ID", legislator.getTwitterId(), bgColor, fgColor, nameFgColor);
        addURLProperty(mainArea, "", getURL("http://twitter.com/${id}", legislator.getTwitterId()), bgColor, linkFgColor, nameFgColor);
        addURLProperty(mainArea, "Congresspedia URL", legislator.getCongresspediaURL(), bgColor, linkFgColor, nameFgColor);
        addURLProperty(mainArea, "YouTube URL", legislator.getYouTubeURL(), bgColor, linkFgColor, nameFgColor);
    }

    @Override
    public void setFocus() {
    }

    /**
     * Sets the default background and foreground colors on the control.
     * 
     * @param control
     *            the control.
     * @param bgColor
     *            the background color to set.
     * @param fgColor
     *            the foreground color to set.
     */
    private void setColors(Control control, Color bgColor, Color fgColor) {
        control.setBackground(bgColor);
        control.setForeground(fgColor);
    }

    /**
     * Returns the legislator displayed by this editor.
     * 
     * @return   the legislator.
     */
    private Legislator getLegislator() {
        LegislatorDetailsEditorInput input = (LegislatorDetailsEditorInput) getEditorInput();
        return input.getLegislator();
    }

    /**
     * Adds a string property to the editor.
     * 
     * @param parent   the parent control.
     * @param name   the name of the property.
     * @param value   the value of the property.
     * @param bgColor   the background color.
     * @param fgColor   the foreground color used for the value.
     * @param nameFgColor   the foreground color used for the name.
     */
    private void addStringProperty(Composite parent, String name, String value, Color bgColor, Color fgColor, Color nameFgColor) {
        if (!StringUtils.isBlank(value)) {
            Label nameLabel = new Label(parent, SWT.NONE);
            GridData nameGridData = new GridData(SWT.FILL, SWT.CENTER, false, false);
            nameGridData.horizontalIndent = 5;
            nameLabel.setLayoutData(nameGridData);
            nameLabel.setBackground(bgColor);
            nameLabel.setForeground(nameFgColor);
            nameLabel.setFont(JFaceResources.getBannerFont());
            nameLabel.setText(name);
            Text valueText = new Text(parent, SWT.SINGLE);
            valueText.setEditable(false);
            valueText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
            valueText.setBackground(bgColor);
            valueText.setForeground(fgColor);
            valueText.setText(value);
        }
    }

    /**
     * Adds a URL property to the editor.
     * 
     * @param parent   the parent control.
     * @param name   the name of the property.
     * @param href   the URL's address.
     * @param bgColor   the background color.
     * @param fgColor   the foreground color used for the value.
     * @param nameFgColor   the foreground color used for the name.
     */
    private void addURLProperty(Composite parent, String name, final String href, Color bgColor, Color fgColor, Color nameFgColor) {
        addURLProperty(parent, name, href, bgColor, fgColor, nameFgColor, false);
    }

    /**
     * Adds a URL property to the editor.
     * 
     * @param parent   the parent control.
     * @param name   the name of the property.
     * @param href   the URL's address.
     * @param bgColor   the background color.
     * @param fgColor   the foreground color used for the value.
     * @param nameFgColor   the foreground color used for the name.
     * @param email   indicates if the URL represents a email address.
     */
    private void addURLProperty(Composite parent, String name, final String href, Color bgColor, Color fgColor, Color nameFgColor,
            boolean email) {
        if (!StringUtils.isBlank(href)) {
            Label nameLabel = new Label(parent, SWT.NONE);
            GridData nameGridData = new GridData(SWT.FILL, SWT.CENTER, false, false);
            nameGridData.horizontalIndent = 5;
            nameLabel.setLayoutData(nameGridData);
            nameLabel.setBackground(bgColor);
            nameLabel.setForeground(nameFgColor);
            nameLabel.setFont(JFaceResources.getBannerFont());
            nameLabel.setText(name);
            final Hyperlink link = new Hyperlink(parent, SWT.SINGLE);
            link.setUnderlined(true);
            GridData linkGridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
            linkGridData.horizontalIndent = 5;
            link.setLayoutData(linkGridData);
            link.setBackground(bgColor);
            link.setForeground(fgColor);
            link.setText(href);
            if (email) {
                link.setHref("mailto:" + href);
            } else {
                link.setHref(href);
            }
            link.addHyperlinkListener(new HyperlinkAdapter() {
                @Override
                public void linkActivated(HyperlinkEvent event) {
                    openBrowser(link.getShell(), (String) link.getHref());
                }
            });
        }
    }

    /**
     * Returns a URL replacing a predefined ${id} token with the <i>id</i> argument.
     * 
     * @param urlTemplate
     *            the URL template, including a ${id} token.
     * @param id
     *            the identifier's value.
     * @return the final URL.
     */
    private String getURL(String urlTemplate, String id) {
        if (!StringUtils.isBlank(id)) {
            return StringUtils.replace(urlTemplate, "${id}", id);
        } else {
            return "";
        }
    }

    /**
     * Opens a browser pointing to a specific address.
     * 
     * @param shell   the parent shell.
     * @param href   the URL's address.
     */
    protected void openBrowser(Shell shell, String href) {
        IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
        try {
            IWebBrowser browser = support.createBrowser(IWorkbenchBrowserSupport.AS_EDITOR | IWorkbenchBrowserSupport.NAVIGATION_BAR
                    | IWorkbenchBrowserSupport.STATUS | IWorkbenchBrowserSupport.LOCATION_BAR, null, null, null);
            browser.openURL(new URL(href));
            if (href.startsWith("mailto:")) {
                browser.close();
            }
        } catch (MalformedURLException e) {
            ExecutionException e2 = new ExecutionException("Unable to open the browser", e);
            ErrorDialog.openError(shell, "Error", e2.getMessage(), new Status(IStatus.ERROR, CongressExplorerPlugin.PLUGIN_ID, e
                    .getMessage(), e));
        } catch (PartInitException e) {
            ExecutionException e2 = new ExecutionException("Unable to open the browser", e);
            ErrorDialog.openError(shell, "Error", e2.getMessage(), new Status(IStatus.ERROR, CongressExplorerPlugin.PLUGIN_ID, e
                    .getMessage(), e));
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.part.WorkbenchPart#dispose()
     */
    @Override
    public void dispose() {
        super.dispose();
        if (photoImage != null) {
            photoImage.dispose();
        }
    }

}
